package org.example.lys.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 正则表达式验证
 *
 * @author ; lidongdong
 * @date 2019-09-23
 */
@Slf4j
public class RegexUtils {

    /**
     * 身份证号前17位对应的加权因子
     */
    public static int[] factor = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    /**
     * sum(身份证号的每位*对应的加权因子)%11 的结果对应数组
     * 余数：         0 1 2 3 4 5 6 7 8 9 10
     * 对应的校验码：  1 0 X 9 8 7 6 5 4 3 2
     */
    public static String[] parity = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};

    /**
     * email 地址
     */
    public final static String REGEXP_EMAIL = "\\w.*@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$";

    /**
     * 年月日
     */
    public final static String REGEXP_DAY = "(\\d){4}-(\\d){2}-(\\d){2}";
    public final static Pattern PATTERN_REGEXP_DAY = Pattern.compile(REGEXP_DAY);

    /**
     * 时分
     */
    public final static String REGEXP_HOUR_MINUTE = "^(?:(?:[0-2][0-3])|(?:[0-1][0-9])):[0-5][0-9]$";
    public final static Pattern PATTERN_REGEXP_HOUR_MINUTE = Pattern.compile(REGEXP_HOUR_MINUTE);

    /**
     * ip地址
     */
    public final static String REGEXP_IP = "^(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5])$";

    /**
     * 汉字
     */
    public final static String REGEXP_CH = "^[\\u4e00-\\u9fa5]*$";

    /**
     * 整数
     */
    public final static String REGEXP_NUM = "^-?\\d+$";
    public final static Pattern PATTERN_REGEXP_NUM = Pattern.compile(REGEXP_NUM);

    /**
     * 手机号码
     */
    public final static String REGEXP_MOBILE = "^[1]([3-9])[0-9]{9}$";

    /**
     * 香港手机号
     */
    public final static String HK_PHONE = "^([5689])\\d{7}$";

    /**
     * 特殊字符
     */
    public final static String SPECIAL_CHAR = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]";

    /**
     * base64字符串
     */
    public final static String BASE_64 = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$";

    /**
     * 匹配[yyyy-MM-dd]的正则表达式
     */
    public final static String REG_YYYY_MM_DD = "(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})" +
            "-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))" +
            "|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)";

    /**
     * 身份证号
     */
    public final static String REGEXP_ID_CARD = "(^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|" +
            "(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)";

    /**
     * 定义正则表达式，域名的根需要自定义，这里不全
     */
    private static final String RE_TOP = "[\\w-]+\\.(com.cn|net.cn|gov.cn|org\\.nz|org.cn|com|net|org|gov|cc|biz|info|cn|co)\\b()*";

    /**
     * 二级域名提取
     */
    private static final String RE_TWO = "(\\w*\\.?){2}\\.(com.cn|net.cn|gov.cn|org\\.nz|org.cn|com|net|org|gov|cc|biz|info|cn|co)$";

    /**
     * 三级域名提取
     */
    private static final String RE_THREE = "(\\w*\\.?){3}\\.(com.cn|net.cn|gov.cn|org\\.nz|org.cn|com|net|org|gov|cc|biz|info|cn|co)$";

    /**
     * 文本的字母和数字
     */
    private static final String REGEXP_LETTER_NUM = "[a-zA-Z0-9]";

    /**
     * 统一社会信用代码
     */
    private static final String REGEXP_USC_CODE = "^([0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}|[1-9]\\d{14})$";

//    private static final String REGEXP_USC_CODE1 = "^(?=.*[0-9].*)(?=.*[A-Z].*).{18,}$";
//    private static final String REGEXP_USC_CODE2 = "^[A-Z0-9]{18}$";

    /**
     * 第一代电子卡式通行证证件号码共11位
     * 第二代通行证号码共9位
     * H开头是香港
     * M开头是澳门
     */
    public static final String H_M_CARD = "^[HM]\\d{10}$|" + "^[HM]\\d{8}$";

    /**
     * 自2008年9月25日起，台胞证号码实行“一人一号、终身不变”，资料页上的“号码”栏为8位终身号码；
     * 多次来往大陆签注和长期居留签注上的“通行证号码”栏仍然为10位号码，前8位为终身号码，紧接2位领证次数，相当于无签发机关代码的旧版台胞证号码
     */
    public static final String TAI_CARD = "\\d{8}|" + "\\d{10}";

    /**
     * 18位证件号-1 or 18位证件号-2
     */
    public static final String NUM_CENTER_NUM = "[0-9xX]{18}-[1-2]";

    /**
     * 匹配 中文 英文 数字 . _ - () （）空格 破折号
     */
    public static final String BATCH_NAME_REGEXP = "^[\\u4e00-\\u9fa5_.—()（）\\s\\-a-zA-Z0-9]+$";

    /**
     * n个数字和1个字母 本系统用与商户名称后缀
     */
    public static final String PATTERN_NAME_SUFFIX = "[0-9]*[D-Z]";

    /**
     * n个数字和1个字母 本系统用与商户名称后缀
     */
    public static final String PATTERN_DISTRIBUTE_NAME_SUFFIX = "[0-9]";

    /**
     * 网络协议正则
     */
    public static final String REGEX_NETWORK_PROTOCOL = "^(http|https).*";
    public static final Pattern PATTERN_NETWORK_PROTOCOL = Pattern.compile(REGEX_NETWORK_PROTOCOL);

    /**
     * 个人银行卡号正则
     */
    public static final String REGEX_PERSONAL_BANK_CARD = "\\d{16,20}";
    public static final Pattern PATTERN_PERSONAL_BANK_CARD = Pattern.compile(REGEX_PERSONAL_BANK_CARD);

    /**
     * 表格后缀
     */
    public static final String REGEX_EXCEL_SUFFIX = "^.*(\\.xls|\\.xlsx)$";
    public static final Pattern PATTERN_EXCEL_SUFFIX = Pattern.compile(REGEX_EXCEL_SUFFIX);


    //------------------------------------------------------------------------------------------------------------------

    /**
     * 18位身份证最后一位校验位验证
     *
     * @param idCard 身份证
     * @return boolean
     */
    public static boolean checkICardDigit(String idCard) {
        if (idCard.length() != 18) {
            return true;
        }
        if (StringUtil.isEmpty(idCard)) {
            return false;
        }
        String[] split = idCard.split("");
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            sum += Integer.parseInt(split[i]) * factor[i];
        }
        return parity[sum % 11].equals(split[17]);
    }

    //------------------------------------------------------------------------------------------------------------------

    /**
     * 检测是否是11位手机号
     *
     * @param phone 电话
     * @return {@link Boolean}
     */
    public static Boolean isPhone(String phone) {
        if (StringUtils.isEmpty(phone)) {
            return false;
        }
        Pattern p = Pattern.compile(REGEXP_MOBILE);
        Matcher m = p.matcher(phone);
        return m.matches();
    }

    /**
     * 获得顶级域名
     *
     * @param url url
     * @return {@link String}
     */
    public static String getTopDomain(String url) {
        String result = url;
        try {
            Matcher matcher = Pattern.compile(RE_TOP, Pattern.CASE_INSENSITIVE).matcher(url);
            matcher.find();
            result = matcher.group();
        } catch (Exception e) {
            log.error("获取顶级域名异常,e:", e);
        }
        return result;
    }


    /**
     * 检测是否是base64串
     *
     * @param base64 base64
     * @return {@link Boolean}
     */
    public static Boolean isBase64(String base64) {
        if (StringUtils.isEmpty(base64)) {
            return false;
        }
        Pattern p = Pattern.compile(BASE_64);
        Matcher m = p.matcher(base64);
        return m.matches();
    }

    /**
     * 检测是否是身份证
     *
     * @param idCard 身份证
     * @return {@link Boolean}
     */
    public static Boolean isIdCard(String idCard) {
        if (StringUtils.isEmpty(idCard)) {
            return false;
        }
        Pattern p = Pattern.compile(REGEXP_ID_CARD);
        Matcher m = p.matcher(idCard);
        return (m.matches() && checkICardDigit(idCard.toUpperCase()));
    }

    /**
     * 检测是否是邮箱
     *
     * @param email 电子邮件
     * @return {@link Boolean}
     */
    public static Boolean isEmail(String email) {
        if (StringUtils.isEmpty(email)) {
            return false;
        }
        Pattern p = Pattern.compile(REGEXP_EMAIL);
        Matcher m = p.matcher(email);
        return m.matches();
    }

    /**
     * 检测是否符合YYYY_MM_DD日期格式
     *
     * @param reg_yyyy_MM_dd reg yyyy mm dd
     * @return {@link Boolean}
     */
    public static Boolean isYYYY_MM_DD(String reg_yyyy_MM_dd) {
        if (StringUtils.isEmpty(reg_yyyy_MM_dd)) {
            return false;
        }
        Pattern p = Pattern.compile(REG_YYYY_MM_DD);
        Matcher m = p.matcher(reg_yyyy_MM_dd);
        return m.matches();
    }

    /**
     * 去除文本里的字母和数字
     *
     * @param originString 源字符串
     * @return {@link String}
     */
    public static String replaceStringLetterAndNum(String originString) {
        if (StringUtils.isEmpty(originString)) {
            return "";
        }
        return originString.replaceAll(REGEXP_LETTER_NUM, "");
    }

    /**
     * 检测是否是统一社会信用代码
     *
     * @param uscCode 南加州大学代码
     * @return {@link Boolean}
     */
    public static Boolean isUscCode(String uscCode) {
        if (StringUtils.isEmpty(uscCode)) {
            return false;
        }
        Pattern p = Pattern.compile(REGEXP_USC_CODE);
        Matcher m = p.matcher(uscCode);
        return m.matches();
    }

    /**
     * 是否是港澳居民来往内地通行证
     *
     * @param card 卡
     * @return {@link Boolean}
     */
    public static Boolean isHMCard(String card) {
        if (StringUtils.isEmpty(card)) {
            return false;
        }
        Pattern p = Pattern.compile(H_M_CARD);
        return p.matcher(card).matches();
    }

    /**
     * 是否是 台湾居民来往大陆通行证
     *
     * @param card 卡
     * @return {@link Boolean}
     */
    public static Boolean isTaiCard(String card) {
        if (StringUtils.isEmpty(card)) {
            return false;
        }
        Pattern p = Pattern.compile(TAI_CARD);
        return p.matcher(card).matches();
    }

    /**
     * 判断该字符串是否是 18位数字+中划线+1位数字
     *
     * @param str 字符串
     * @return Boolean
     */
    public static Boolean isNumCenterNum(String str) {
        if (StringUtils.isEmpty(str)) {
            return false;
        }
        return Pattern.compile(NUM_CENTER_NUM).matcher(str).matches();
    }

    /**
     * 匹配 中文 英文 数字 . _ - () （）空格 破折号
     * 目前用于文件名称和批次名称的匹配
     *
     * @param str str
     * @return {@link Boolean}
     */
    public static Boolean isBatchName(String str) {
        if (StringUtils.isEmpty(str)) {
            return false;
        }
        return Pattern.compile(BATCH_NAME_REGEXP).matcher(str).matches();
    }

    /**
     * 去掉商户名称里的字母和数字
     *
     * @param name 的名字
     * @return {@link String}
     */
    public static String cooperatorNameFilter(String name) {
        if (StringUtils.isEmpty(name)) {
            return name;
        }
        return name.replaceAll(PATTERN_NAME_SUFFIX, "");
    }

    /**
     * 去掉商户名称里的字母和数字
     *
     * @param name 的名字
     * @return {@link String}
     */
    public static String distributeNameFilter(String name) {
        if (StringUtils.isEmpty(name)) {
            return name;
        }
        return name.replaceAll(PATTERN_DISTRIBUTE_NAME_SUFFIX, "");
    }

    /**
     * 香港手机号码8位数，5|6|8|9开头+7位任意数
     *
     * @param str str
     * @return boolean
     */
    public static boolean isHKPhoneLegal(String str) {
        Pattern p = Pattern.compile(HK_PHONE);
        Matcher m = p.matcher(str);
        return m.matches();
    }

    /**
     * 校验是否全部是数字
     *
     * @param str str
     * @return boolean
     */
    public static boolean isNum(String str) {
        return Pattern.compile(REGEXP_NUM).matcher(str).matches();
    }

    /**
     * 校验网址正确
     *
     * @param str str
     * @return boolean
     */
    public static boolean isValidUrl(String str) {
        return PATTERN_NETWORK_PROTOCOL.matcher(str).matches();
    }

    /**
     * 校验个人银行卡号
     *
     * @param str str
     * @return boolean
     */
    public static boolean isPersonalBankCard(String str) {
        return PATTERN_PERSONAL_BANK_CARD.matcher(str).matches();
    }

}
